﻿using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using PolyVoxCore;
using System;

namespace PolyXNAx
{
    public class PolyXNAxVolume
    {
        int blocksize = 25;
        int blockcount;
        private SimpleVolumeMaterialDensityPair62 pVolume;
        private SurfaceMeshPositionMaterialNormal pSurface;

        private bool hasChanged;
        private PolyVoxBatchPart[,,] pBatch;
        private bool[, ,] pNeedUpdate;

        private Vector3DInt32 pPosition;
        private Vector3DInt32 pPosition2;
        public PolyXNAxVolume()
        {
            pVolume = new SimpleVolumeMaterialDensityPair62(new Region(new Vector3DInt32(0, 0, 0), new Vector3DInt32(750, 750, 750)));
            pVolume.setBorderValue(new MaterialDensityPair62(0, 0));
            pSurface = new SurfaceMeshPositionMaterialNormal();

            pPosition = new Vector3DInt32(0,0,0);
            pPosition2 = new Vector3DInt32(0,0,0);
            
            blockcount = 750 / blocksize;
            pBatch = new PolyVoxBatchPart[blockcount,blockcount,blockcount];
            pNeedUpdate = new bool[blockcount, blockcount, blockcount];
            hasChanged = false;
            for (int x = 0; x < blockcount; ++x)
            {
                for (int y = 0; y < blockcount; ++y)
                {
                    for (int z = 0; z < blockcount; ++z)
                    {
                        pBatch[x, y, z] = new PolyVoxBatchPart(new Vector3(x * blocksize, y * blocksize, z * blocksize), blocksize);
                    }
                }
            }
        }

        private void calculate(GraphicsDevice device)
        {
            if (hasChanged)
            {
                hasChanged = false;
                for (int x = 0; x < blockcount; ++x)
                {
                    for (int y = 0; y < blockcount; ++y)
                    {
                        for (int z = 0; z < blockcount; ++z)
                        {
                            if (pNeedUpdate[x, y, z])
                            {
                                pBatch[x, y, z].calculate(pVolume, device);
                                pNeedUpdate[x, y, z] = false;
                            }
                        }
                    }
                }
              
            }
            
        }
        public void Draw(GraphicsDevice device, BoundingFrustum frustum, BasicEffect effect)
        {
            
            #region multiple draw calls
            for (int x = 0; x < blockcount; ++x)
            {
                for (int y = 0; y < blockcount; ++y)
                {
                    for (int z = 0; z < blockcount; ++z)
                    {
                        pBatch[x, y, z].Draw(device, frustum, effect);
                    }
                }
            }
            #endregion
        }

public void Update(GraphicsDevice device)
{
    this.calculate(device);
}
        
        public void SetVoxel(Vector3 position, MaterialDensityPair62 voxel)
        {
            int x = (int)position.X + 375;
            int y = (int)position.Y + 375;
            int z = (int)position.Z + 375;
            if (x > 0 && y > 0 && z > 0 &&
                x < 750 && y < 750 && z < 750)
            {
                pPosition.setX(x);
                pPosition.setY(y);
                pPosition.setZ(z);
                MaterialDensityPair62 oldVoxel = pVolume.getVoxelAt(pPosition);
                if (!oldVoxel.equals(voxel))
                {
                    pVolume.setVoxelAt(pPosition, voxel);
                    if (blocksize == 50)
                    {
                        x = x * 41 >> 11;
                        y = y * 41 >> 11;
                        z = z * 41 >> 11;
                    }
                    if (blocksize == 25)
                    {
                        x = x * 41 >> 10;
                        y = y * 41 >> 10;
                        z = z * 41 >> 10;
                    }
                    if (blocksize == 10)
                    {
                        x = x * 205 >> 11;
                        y = y * 205 >> 11;
                        z = z * 205 >> 11;
                    }
                    pNeedUpdate[x, y, z] = true;

                    hasChanged = true;
                }
            }
        
        }

        public void SetBlock(Vector3 CornerA, Vector3 CornerB, MaterialDensityPair62 voxel)
        {
            Vector3 lower = new Vector3(Math.Min(CornerA.X, CornerB.X), Math.Min(CornerA.Y, CornerB.Y), Math.Min(CornerA.Z, CornerB.Z));
            Vector3 upper = new Vector3(Math.Max(CornerA.X, CornerB.X), Math.Max(CornerA.Y, CornerB.Y), Math.Max(CornerA.Z, CornerB.Z));
            int x = (int)lower.X + 375;
            int y = (int)lower.Y + 375;
            int z = (int)lower.Z + 375;
            int x2 = (int)upper.X + 375;
            int y2 = (int)upper.Y + 375;
            int z2 = (int)upper.Z + 375;
            if (x > 0 && y > 0 && z > 0 &&
                x < 750 && y < 750 && z < 750
                &&
                x2 > 0 && y2 > 0 && z2 > 0 &&
                    x2 < 750 && y2 < 750 && z2 < 750)
            {
                pPosition.setX(x);
                pPosition.setY(y);
                pPosition.setZ(z);


                pPosition2.setX(x2);
                pPosition2.setY(y2);
                pPosition2.setZ(z2);

                pVolume.setBlockAt(pPosition, pPosition2, voxel);
                if (blocksize == 50)
                {
                    x = x * 41 >> 11;
                    y = y * 41 >> 11;
                    z = z * 41 >> 11;

                    x2 = x2 * 41 >> 11;
                    y2 = y2 * 41 >> 11;
                    z2 = z2 * 41 >> 11;
                }
                if (blocksize == 25)
                {
                    x = x * 41 >> 10;
                    y = y * 41 >> 10;
                    z = z * 41 >> 10;

                    x2 = x2 * 41 >> 10;
                    y2 = y2 * 41 >> 10;
                    z2 = z2 * 41 >> 10;
                }
                if (blocksize == 10)
                {
                    x = x * 205 >> 11;
                    y = y * 205 >> 11;
                    z = z * 205 >> 11;

                    x2 = x2 * 205 >> 11;
                    y2 = y2 * 205 >> 11;
                    z2 = z2 * 205 >> 11;
                }
                for (int cx = 0; cx <= x2 - x; ++cx)
                {
                    for (int cy = 0; cy <= y2 - y; ++cy)
                    {
                        for (int cz = 0; cz <= z2 - z; ++cz)
                        {
                            this.pNeedUpdate[x + cx, y + cy, z + cz] = true;
                        }
                    }
                }
            }
            
        }
        public void SetSphere(Vector3 center, int radius,MaterialDensityPair62 voxel)
        {
              int fx = (int)center.X + 375;
            int fy = (int)center.Y + 375;
            int fz = (int)center.Z + 375;

            if (fx > 0 + radius && fy > 0 + radius && fz > 0 + radius &&
               fx < 750 - radius && fy < 750 - radius && fz < 750 - radius
            )
            {
                pPosition.setX(fx);
                pPosition.setY(fy);
                pPosition.setZ(fz);
                pVolume.setSphereAt(pPosition, radius, voxel);
                if (blocksize == 50)
                {
                    fx = fx * 41 >> 11;
                    fy = fy * 41 >> 11;
                    fz = fz * 41 >> 11;
                }
                if (blocksize == 25)
                {
                    fx = fx * 41 >> 10;
                    fy = fy * 41 >> 10;
                    fz = fz * 41 >> 10;
                }
                if (blocksize == 10)
                {
                    fx = fx * 205 >> 11;
                    fy = fy * 205 >> 11;
                    fz = fz * 205 >> 11;
                }
                for (int x = -radius / blocksize ; x <= radius / blocksize ; ++x)
                {
                    for (int y = -radius / blocksize ; y <= radius / blocksize ; ++y)
                    {
                        for (int z = -radius / blocksize ; z <= radius / blocksize; ++z)
                        {
                         
                            pNeedUpdate[fx +x, fy+y, fz +z] = true;
                        }
                    }
                }


          
              
                hasChanged = true;
            }
        }
        public void DrawLine(Vector3 from, Vector3 to, MaterialDensityPair62 voxel)
        {
            int fx = (int)from.X + 375;
            int fy = (int)from.Y + 375;
            int fz = (int)from.Z + 375;
            int tx = (int)to.X + 375;
            int ty = (int)to.Y + 375;
            int tz = (int)to.Z + 375;
            if (fx > 0 && fy > 0 && fz > 0 &&
               fx < 750 && fy < 750 && fz < 750
                &&
                tx > 0 && ty > 0 && tz > 0 &&
               tx < 750 && ty < 750 && tz < 750)
            {
                pPosition.setX(fx);
                pPosition.setY(fy);
                pPosition.setZ(fz);
                pPosition2.setX(tx);
                pPosition2.setY(ty);
                pPosition2.setZ(tz);

                pVolume.DrawLine(pPosition,pPosition2, voxel);
                if (blocksize == 50)
                {
                    fx = fx * 41 >> 11;
                    fy = fy * 41 >> 11;
                    fz = fz * 41 >> 11;
                    tx = tx * 41 >> 11;
                    ty = ty * 41 >> 11;
                    tz = tz * 41 >> 11;
                }
                if (blocksize == 25)
                {
                    fx = fx * 41 >> 10;
                    fy = fy * 41 >> 10;
                    fz = fz * 41 >> 10;
                    tx = tx * 41 >> 10;
                    ty = ty * 41 >> 10;
                    tz = tz * 41 >> 10;
                }
                if (blocksize == 10)
                {
                    fx = fx * 205 >> 11;
                    fy = fy * 205 >> 11;
                    fz = fz * 205 >> 11;
                    tx = tx * 205 >> 11;
                    ty = ty * 205 >> 11;
                    tz = tz * 205 >> 11;
                }
                for (int x = 0; x <  tx-fx+1; ++x)
                {
                    for (int y = 0; y < ty - fy+1; ++y)
                    {
                        for (int z = 0; z < tz - fz+1; ++z)
                        {
                            pNeedUpdate[fx+x, fy+y, fz+z] = true;
                        }
                    }
                }
               
                hasChanged = true;
            }
        }
      /*  private Vector3 vector3DFloatToVector3(Vector3DFloat vector3DFloat)
        {
            return new Vector3(vector3DFloat.getX(), vector3DFloat.getY(), vector3DFloat.getZ());
        }*/
    }
}
